package com.hm.hall.commons.util;

import com.alibaba.fastjson.JSON;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.CoreConnectionPNames;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;
import org.apache.log4j.Logger;
import org.springframework.scheduling.annotation.Async;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.*;

/**
 * @author wei
 */
public class HttpClientUtils {

    static Logger log = Logger.getLogger(HttpClientUtils.class);

    private static ThreadSafeClientConnManager connManager = new ThreadSafeClientConnManager();

    static {
        // Increase max total connection to 200
        connManager.setMaxTotal(200);
        // Increase default max connection per route to 20
        connManager.setDefaultMaxPerRoute(150);
    }

    /**
     * 获取httpclient  5秒连接超时，5秒数据超时
     *
     * @return DefaultHttpClient
     */
    public static DefaultHttpClient getClient() {
        DefaultHttpClient client = new DefaultHttpClient(connManager);
        client.getParams().setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 5000);//连接时间5s
        client.getParams().setParameter(CoreConnectionPNames.SO_TIMEOUT, 5000);
        return client;
    }

    /**
     * 执行 request 请求
     *
     * @param params         参数
     * @param url            url
     * @param key_retryCount 重试次数
     * @return
     */
    public static String requsetExecute(Map<String, Object> params, String url, int key_retryCount) {
        DefaultHttpClient httpclient = HttpClientUtils.getClient();
        DefaultHttpRequestRetryHandler retryHandler = new DefaultHttpRequestRetryHandler(key_retryCount, true);
        httpclient.setHttpRequestRetryHandler(retryHandler);
        httpclient.getParams().setParameter(CoreConnectionPNames.SO_TIMEOUT, 10000);//10s超时
        HttpResponse response;

        HttpPost httpost = new HttpPost(url);
        List<NameValuePair> nvps = new ArrayList<NameValuePair>();

        //固定值
        for (String key : params.keySet()) {
            nvps.add(new BasicNameValuePair(key, "" + params.get(key)));
        }
        try {
            httpost.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
            response = httpclient.execute(httpost);
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                return EntityUtils.toString(entity, "UTF-8");
            }
        } catch (Exception e) {
            log.error("deal failed! URL:" + url, e);
        }
        return null;

    }

    /**
     * 执行 request 请求
     *
     * @param url            url
     * @param key_retryCount 重试次数
     * @return
     */
    public static String requsetGetExecute(String url, int key_retryCount) {
        DefaultHttpClient httpclient = HttpClientUtils.getClient();
        DefaultHttpRequestRetryHandler retryHandler = new DefaultHttpRequestRetryHandler(key_retryCount, true);
        httpclient.setHttpRequestRetryHandler(retryHandler);
        httpclient.getParams().setParameter(CoreConnectionPNames.SO_TIMEOUT, 10000);//10s超时
        HttpResponse response;

        HttpGet httget = new HttpGet(url);
        try {
            response = httpclient.execute(httget);
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                return EntityUtils.toString(entity, "UTF-8");
            }
        } catch (Exception e) {
            log.error("deal failed! URL:" + url, e);
        }
        return null;

    }

    /**
     * 带证书的请求 https
     * 例:
     * HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack");
     * List<NameValuePair> nvps = new ArrayList<>();
     * for (String key : params.keySet()) {
     * nvps.add(new BasicNameValuePair(key, params.get(key) + ""));
     * }
     * httpPost.setEntity(new UrlEncodedFormEntity(nvps));
     * requsetCertHttps("10016225", "PKCS12", "TLSv1", "weixin_mp/pay_cert/apiclient_cert.p12", httpPost);
     *
     * @param PKCS12PWD        指定PKCS12的密码(商户ID)
     * @param keyStoreType     指定读取证书格式为PKCS12
     * @param tlsVersion       指定TLS版本
     * @param certResourcePath 读取本机存放的PKCS12证书文件路径 ,相对于项目资源文件路径
     * @param request          请求对象
     * @throws Exception
     */
    public static String requsetCertHttps(String PKCS12PWD, String keyStoreType, String tlsVersion, String certResourcePath, HttpUriRequest request) throws Exception {
        HttpClient httpClient = HttpClientUtils.getClient();
        //加载证书
        KeyStore keyStore = KeyStore.getInstance(keyStoreType);
        // 读取本机存放的PKCS12证书文件 ,相对于项目资源文件路径
        InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(certResourcePath);
        try {
            //指定PKCS12的密码(商户ID)
            keyStore.load(inputStream, PKCS12PWD.toCharArray());
        } finally {
            inputStream.close();
        }

        //创建jkd密钥访问库
        KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
        kmf.init(keyStore, PKCS12PWD.toCharArray());
        //asdfgh是key密码。
        //创建管理jks密钥库的x509密钥管理器，用来管理密钥，需要key的密码
        SSLContext sslc = SSLContext.getInstance(tlsVersion);
        // 构造SSL环境，指定SSL版本为3.0，也可以使用TLSv1，但是SSLv3更加常用。
        sslc.init(kmf.getKeyManagers(), null, null);

        SSLSocketFactory socketFactory = new SSLSocketFactory(sslc, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

        //这个8446是和被访问端约定的端口，一般为443
        Scheme sch = new Scheme("https", socketFactory, 443);
        httpClient.getConnectionManager().getSchemeRegistry().register(sch);

        String result = null;
        HttpResponse httpResponse = httpClient.execute(request);
        HttpEntity entity = httpResponse.getEntity();
        int re_code = httpResponse.getStatusLine().getStatusCode();
        if (re_code == 200) {
            result = EntityUtils.toString(entity, "UTF-8");
        }
        return result;
    }

    public static String doRawPostSsl(Map<String, Object> params, String url) {
        try {

            HttpClient httpclient = HttpClientUtils.getClient();
            //Secure Protocol implementation.
            SSLContext ctx = SSLContext.getInstance("SSL");
            //Implementation of a trust manager for X509 certificates
            X509TrustManager tm = new X509TrustManager() {
                @Override
                public void checkClientTrusted(X509Certificate[] xcs,
                                               String string) throws CertificateException {

                }

                @Override
                public void checkServerTrusted(X509Certificate[] xcs,
                                               String string) throws CertificateException {
                }

                @Override
                public X509Certificate[] getAcceptedIssuers() {
                    return null;
                }
            };
            ctx.init(null, new TrustManager[]{tm}, null);
            SSLSocketFactory ssf = new SSLSocketFactory(ctx);

            ClientConnectionManager ccm = httpclient.getConnectionManager();
            //register https protocol in httpclient's scheme registry
            SchemeRegistry sr = ccm.getSchemeRegistry();
            sr.register(new Scheme("https", 443, ssf));

            HttpPost httpost = new HttpPost(url);

            StringEntity postingString = new StringEntity(JSON.toJSONString(params), "utf-8");// json传递

            postingString.setContentType("application/json");
            postingString.setContentEncoding("utf-8");
            System.out.println(JSON.toJSONString(params));
            httpost.setEntity(postingString);

//            httpost.addHeader("Accept", "application/json");
//            httpost.addHeader("Content-Type","application/json;charset=utf-8");

            HttpResponse response = httpclient.execute(httpost);
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                return EntityUtils.toString(entity, "UTF-8");
            }

            // Create a response handler  

        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception ex) {
            ex.printStackTrace();

        }
        return null;
    }

    /**
     * 执行 request 请求
     *
     * @param params         参数
     * @param url            url
     * @param key_retryCount 重试次数
     * @return
     */

    /**
     * 执行 request 请求
     *
     * @param params         参数
     * @param url            url
     * @param key_retryCount 重试次数
     * @return
     */
    public static String requsetPostExecute(HashMap<String, String> params, HashMap<String, String> cookies, String url, int key_retryCount) {
        CloseableHttpClient httpclient = HttpClientUtils.getClient();
        DefaultHttpRequestRetryHandler retryHandler = new DefaultHttpRequestRetryHandler(key_retryCount, true);
        // httpclient.setHttpRequestRetryHandler(retryHandler);
        httpclient.getParams().setParameter(CoreConnectionPNames.SO_TIMEOUT, 10000);//10s超时
        CloseableHttpResponse response = null;


        HttpPost httpost = new HttpPost(url);
        httpost.setProtocolVersion(HttpVersion.HTTP_1_0);
        httpost.addHeader(HTTP.CONN_DIRECTIVE, HTTP.CONN_CLOSE);
        httpost.addHeader("NeedEnc", "No");
        List<NameValuePair> nvps = new ArrayList<NameValuePair>();

        //固定值
        for (String key : params.keySet()) {
            nvps.add(new BasicNameValuePair(key, "" + params.get(key)));
        }

        // 设置cookie
        if (cookies != null && !cookies.isEmpty()) {
            Iterator iter = cookies.entrySet().iterator();
            StringBuilder buffer = new StringBuilder(128);
            while (iter.hasNext()) {
                Map.Entry entry = (Map.Entry) iter.next();
                buffer.append((String) entry.getKey()).append("=").append((String) entry.getValue()).append("; ");
            }
            // 设置cookie内容
            httpost.addHeader("Cookie", buffer.toString());
        }


        try {
            httpost.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
            response = httpclient.execute(httpost);
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                String resp = EntityUtils.toString(entity, "UTF-8");
                log.info("req url :" + url + "  resp:" + resp);
                EntityUtils.consume(entity);        //按照官方文档的说法：二者都释放了才可以正常的释放链接   
                response.close();
                return resp;
            }
        } catch (Exception e) {
            log.error("deal failed! URL:" + url + "   " + e.getMessage());
        } finally {
            if (response != null) {
                try {
                    response.close();
                } catch (IOException e) {
                    log.error("deal close failed! URL:" + url + "   " + e.getMessage());
                }
            }

        }
        return null;

    }


    /**
     * 执行 request 请求
     *
     * @param params         参数
     * @param url            url
     * @param key_retryCount 重试次数
     * @return
     */
    @Async
    public String requsetPostExecuteAsync(HashMap<String, String> params, HashMap<String, String> cookies, String url, int key_retryCount) {
        CloseableHttpClient httpclient = HttpClientUtils.getClient();
        DefaultHttpRequestRetryHandler retryHandler = new DefaultHttpRequestRetryHandler(key_retryCount, true);
        // httpclient.setHttpRequestRetryHandler(retryHandler);
        httpclient.getParams().setParameter(CoreConnectionPNames.SO_TIMEOUT, 10000);//10s超时
        HttpResponse response;

        HttpPost httpost = new HttpPost(url);
        List<NameValuePair> nvps = new ArrayList<NameValuePair>();

        //固定值
        for (String key : params.keySet()) {
            nvps.add(new BasicNameValuePair(key, "" + params.get(key)));
        }

        // 设置cookie
        if (cookies != null && !cookies.isEmpty()) {
            Iterator iter = cookies.entrySet().iterator();
            StringBuilder buffer = new StringBuilder(128);
            while (iter.hasNext()) {
                Map.Entry entry = (Map.Entry) iter.next();
                buffer.append((String) entry.getKey()).append("=").append((String) entry.getValue()).append("; ");
            }
            // 设置cookie内容
            httpost.addHeader("Cookie", buffer.toString());
        }

        try {
            httpost.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
            response = httpclient.execute(httpost);
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                String resp = EntityUtils.toString(entity, "UTF-8");
                log.info("req url :" + url + "  resp:" + resp);
                return resp;
            }
        } catch (Exception e) {
            log.error("deal failed! URL:" + url, e);
        }
        return null;

    }
}

